#include "core/rpc_generator.h"

#include <map>
#include <sstream>

namespace asfinger {
namespace pbrpc {

namespace {

/*!
 * 转换成字符串
 * @tparam T 要转换的类型
 * @param x 要转换的对象
 * @return 转换后的字符串
 */
template<class T>
std::string AsString(T x) {
  std::ostringstream out;
  out << x;
  return out.str();
}

/*!
 * 仅为客户端缓冲流
 * @param method protobuf函数
 * @return true 仅为客户端缓冲流
 * @return false 非仅为客户端缓冲流
 */
inline bool IsOnlyClientStreaming(const MethodInterface *method) {
  return method->IsClientStreaming() && !method->IsServerStreaming();
}

/*!
 * 仅为服务端缓冲流
 * @param method protobuf函数
 * @return true 仅为服务端缓冲流
 * @return false 非仅为服务端缓冲流
 */
inline bool IsOnlyServerStreaming(const MethodInterface *method) {
  return !method->IsClientStreaming() && method->IsServerStreaming();
}

/*!
 * 文件描述符
 * @param filename 文件名
 * @return 文件描述符
 */
std::string FilenameIdentifier(const std::string &filename) {
  std::string result;
  for (char c : filename) {
    if (isalnum(c)) {
      result.push_back(c);
    } else {
      static char hex[] = "0123456789abcdef";
      result.push_back('_');
      result.push_back(hex[(c >> 4) & 0xf]);
      result.push_back(hex[c & 0xf]);
    }
  }
  return result;
}

}  //! namespace

/*!
 * 返回array结尾
 * @tparam T 类型
 * @tparam N array长度
 * @param array 数组
 * @return array结尾指针
 */
template<class T, size_t N>
T *ArrayEnd(T (&array)[N]) {
  return array + N;
}

/*!
 * 打印头文件包含
 * @param printer 打印器
 * @param headers 头文件列表
 * @param use_system_headers 是否使用
 * @param search_path 查找路径
 */
void PrintIncludes(Printer *printer,
                   const std::vector<std::string> &headers,
                   bool use_system_headers, const std::string &search_path) {
  std::map<std::string, std::string> vars;

  vars["l"] = use_system_headers ? '<' : '"';
  vars["r"] = use_system_headers ? '>' : '"';

  if (!search_path.empty()) {
    vars["l"] += search_path;
    if (search_path[search_path.size() - 1] != '/') {
      vars["l"] += '/';
    }
  }

  for (const auto &header : headers) {
    vars["h"] = header;
    printer->Print(vars, "#include $l$$h$$r$\n");
  }
}

/*!
 * 打印头文件客户端方法接口
 * @param printer 打印器
 * @param method 方法
 * @param vars 参数
 * @param is_virtual 是否为虚函数
 */
void PrintHeaderClientMethodInterfaces(Printer *printer,
                                       const MethodInterface *method,
                                       std::map<std::string, std::string> &vars,
                                       bool is_virtual) {
  vars["Method"] = method->GetName();
  vars["Request"] = method->GetInputTypeClassName();
  vars["Response"] = method->GetOutputTypeClassName();

  if (is_virtual) {
    printer->Print(vars,
                   "virtual void $Method$(\n"
                   " google::protobuf::RpcController* controller,\n"
                   " const $Request$* request,\n"
                   " $Response$* response,\n"
                   " google::protobuf::Closure* done);\n\n");
  } else {
    printer->Print(vars,
                   "void $Method$(\n"
                   "  google::protobuf::RpcController* controller,\n"
                   "  const $Request$* request,\n"
                   "  $Response$* response,\n"
                   "  google::protobuf::Closure* done) override;\n\n");
  }
}

/*!
 * 打印头文件客户端方法
 * @param printer 打印器
 * @param method 方法
 * @param vars 参数
 * @param is_public 是否为共有
 */
void PrintHeaderClientMethod(Printer *printer,
                             const MethodInterface *method,
                             std::map<std::string, std::string> &vars,
                             bool is_public) {
  vars["Method"] = method->GetName();
  vars["Request"] = method->GetInputTypeClassName();
  vars["Response"] = method->GetOutputTypeClassName();

  /*!< 异步前缀 */
  struct {
    std::string prefix; /*!< 前缀 */
    std::string method_params; /*!< 方法额外参数 */
    std::string raw_args; /*!< 方法原始版本额外参数 */
  } async_prefixes[] = {{"Async", ", void* tag", ", tag"},
                        {"PrepareAsync", "", ""}};

  if (is_public) { // 共有方法
    if (method->IsNoStreaming()) { // 非缓冲流
      printer->Print(vars,
                     "rpc::Status $Method$(rpc::ClientContext* context, "
                     "const $Request$& request, $Response$* response) override;\n");
      for (const auto &async_prefix : async_prefixes) {
        vars["AsyncPrefix"] = async_prefix.prefix;
        printer->Print(vars,
                       "std::unique_ptr< ::rpc::ClientAsyncResponseReader< $Response$>> "
                       "$AsyncPrefix$$Method$(::rpc::ClientContext* context, "
                       "const $Request$& request, "
                       "rpc::CompletionQueue* cq) {\n");
        printer->Indent();
        printer->Print(vars,
                       "return std::unique_ptr< "
                       "rpc::ClientAsyncResponseReader< $Response$>>("
                       "$AsyncPrefix$$Method$Raw(context, request, cq));\n");
        printer->Outdent();
        printer->Print("}\n");
      }
    } else if (IsOnlyClientStreaming(method)) { // 仅客户端缓冲流
      printer->Print(vars,
                     "std::unique_ptr< rpc::ClientWriter< $Request$>>"
                     " $Method$("
                     "rpc::ClientContext* context, $Response$* response) {\n");
      printer->Indent();
      printer->Print(vars,
                     "return std::unique_ptr< rpc::ClientWriter< $Request$>>"
                     "($Method$Raw(context, response));\n");
      printer->Outdent();
      printer->Print("}\n");
      for (const auto &async_prefix : async_prefixes) {
        vars["AsyncPrefix"] = async_prefix.prefix;
        vars["AsyncMethodParams"] = async_prefix.method_params;
        vars["AsyncRawArgs"] = async_prefix.raw_args;
        printer->Print(vars,
                       " $AsyncPrefix$$Method$(::rpc::ClientContext* context, "
                       "$Response$* response, "
                       "rpc::CompletionQueue* cq$AsyncMethodParams$) {\n");
        printer->Indent();
        printer->Print(vars,
                       "return std::unique_ptr< rpc::ClientAsyncWriter< $Request$>>("
                       "$AsyncPrefix$$Method$Raw(context, response, "
                       "cq$AsyncRawArgs$));\n");
        printer->Outdent();
        printer->Print("}\n");
      }
    } else if (IsOnlyServerStreaming(method)) { // 仅服务器缓冲流
      printer->Print(vars,
                     "std::unique_ptr< ::rpc::ClientReader< $Response$>>"
                     " $Method$(::rpc::ClientContext* context, const $Request$& request)"
                     " {\n");
      printer->Indent();
      printer->Print(vars,
                     "return std::unique_ptr< ::rpc::ClientReader< $Response$>>"
                     "($Method$Raw(context, request));\n");
      printer->Outdent();
      printer->Print("}\n");
      for (const auto &async_prefix : async_prefixes) {
        vars["AsyncPrefix"] = async_prefix.prefix;
        vars["AsyncMethodParams"] = async_prefix.method_params;
        vars["AsyncRawArgs"] = async_prefix.raw_args;
        printer->Print(vars,
                       "std::unique_ptr< ::rpc::ClientAsyncReader< $Response$>> "
                       "$AsyncPrefix$$Method$("
                       "::rpc::ClientContext* context, const $Request$& request, "
                       "::rpc::CompletionQueue* cq$AsyncMethodParams$) {\n");
        printer->Indent();
        printer->Print(vars,
                       "return std::unique_ptr< ::rpc::ClientAsyncReader< $Response$>>("
                       "$AsyncPrefix$$Method$Raw(context, request, cq$AsyncRawArgs$));\n");
        printer->Outdent();
        printer->Print("}\n");
      }
    } else if (method->IsBidiStreaming()) { // 双向缓冲流
      printer->Print(vars,
                     "std::unique_ptr< ::rpc::ClientReaderWriter< $Request$, $Response$>>"
                     " $Method$(::rpc::ClientContext* context) {\n");
      printer->Indent();
      printer->Print(vars,
                     "return std::unique_ptr< "
                     "::rpc::ClientReaderWriter< $Request$, $Response$>>("
                     "$Method$Raw(context));\n");
      printer->Outdent();
      printer->Print("}\n");
      for (const auto &async_prefix : async_prefixes) {
        vars["AsyncPrefix"] = async_prefix.prefix;
        vars["AsyncMethodParams"] = async_prefix.method_params;
        vars["AsyncRawArgs"] = async_prefix.raw_args;
        printer->Print(vars,
                       "std::unique_ptr<  ::rpc::ClientAsyncReaderWriter< "
                       "$Request$, $Response$>> "
                       "$AsyncPrefix$$Method$(::rpc::ClientContext* context, "
                       "::rpc::CompletionQueue* cq$AsyncMethodParams$) {\n");
        printer->Indent();
        printer->Print(vars,
                       "return std::unique_ptr< "
                       "::rpc::ClientAsyncReaderWriter< $Request$, $Response$>>("
                       "$AsyncPrefix$$Method$Raw(context, cq$AsyncRawArgs$));\n");
        printer->Outdent();
        printer->Print("}\n");
      }
    }
  } else { // 非共有方法
    if (method->IsNoStreaming()) { // 非缓冲流
      for (const auto &async_prefix : async_prefixes) {
        vars["AsyncPrefix"] = async_prefix.prefix;
        printer->Print(vars,
                       "::rpc::ClientAsyncResponseReader< $Response$>* "
                       "$AsyncPrefix$$Method$Raw(::rpc::ClientContext* context, "
                       "const $Request$& request, "
                       "::rpc::CompletionQueue* cq) override;\n");
      }
    } else if (IsOnlyClientStreaming(method)) { // 仅客户端缓冲流
      printer->Print(vars,
                     "::rpc::ClientWriter< $Request$>* $Method$Raw("
                     "::rpc::ClientContext* context, $Response$* response) "
                     "override;\n");
      for (const auto &async_prefix : async_prefixes) {
        vars["AsyncPrefix"] = async_prefix.prefix;
        vars["AsyncMethodParams"] = async_prefix.method_params;
        vars["AsyncRawArgs"] = async_prefix.raw_args;
        printer->Print(vars,
                       "::rpc::ClientAsyncWriter< $Request$>* $AsyncPrefix$$Method$Raw("
                       "::rpc::ClientContext* context, $Response$* response, "
                       "::rpc::CompletionQueue* cq$AsyncMethodParams$) override;\n");
      }
    } else if (IsOnlyServerStreaming(method)) { // 仅服务器缓冲流
      printer->Print(vars,
                     "::rpc::ClientReader< $Response$>* $Method$Raw("
                     "::rpc::ClientContext* context, const $Request$& request)"
                     " override;\n");
      for (const auto &async_prefix : async_prefixes) {
        vars["AsyncPrefix"] = async_prefix.prefix;
        vars["AsyncMethodParams"] = async_prefix.method_params;
        vars["AsyncRawArgs"] = async_prefix.raw_args;
        printer->Print(vars,
                       "::rpc::ClientAsyncReader< $Response$>* $AsyncPrefix$$Method$Raw("
                       "::rpc::ClientContext* context, const $Request$& request, "
                       "::rpc::CompletionQueue* cq$AsyncMethodParams$) override;\n");
      }
    } else if (method->IsBidiStreaming()) { // 双向缓冲流
      printer->Print(vars,
                     "::rpc::ClientReaderWriter< $Request$, $Response$>* "
                     "$Method$Raw(::rpc::ClientContext* context) override;\n");
      for (const auto &async_prefix : async_prefixes) {
        vars["AsyncPrefix"] = async_prefix.prefix;
        vars["AsyncMethodParams"] = async_prefix.method_params;
        vars["AsyncRawArgs"] = async_prefix.raw_args;
        printer->Print(vars,
                       "::rpc::ClientAsyncReaderWriter< $Request$, $Response$>* "
                       "$AsyncPrefix$$Method$Raw(::rpc::ClientContext* context, "
                       "::rpc::CompletionQueue* cq$AsyncMethodParams$) override;\n");
      }
    }
  }
}

/*!
 * 打印头文件客户端方法数据
 * @param printer 打印器
 * @param method 方法
 * @param vars 参数
 */
void PrintHeaderClientMethodData(Printer *printer,
                                 const MethodInterface *method,
                                 std::map<std::string, std::string> &vars) {
  vars["Method"] = method->GetName();
  printer->Print(vars,
                 "const ::rpc::internal::RpcMethod rpcmethod_$Method$_;\n");
}

/*!
 * 打印头文件服务端同步方法
 * @param printer 打印器
 * @param method 方法
 * @param vars 参数
 */
void PrintHeaderServerMethodSync(Printer *printer,
                                 const MethodInterface *method,
                                 std::map<std::string, std::string> &vars) {
  vars["Method"] = method->GetName();
  vars["Request"] = method->GetInputTypeClassName();
  vars["Response"] = method->GetOutputTypeClassName();
  printer->Print(method->GetLeadingComments("//").c_str());
  if (method->IsNoStreaming()) {
    printer->Print(vars,
                   "virtual ::rpc::Status $Method$("
                   "::rpc::ServerContext* context, const $Request$* request, "
                   "$Response$* response);\n");
  } else if (IsOnlyClientStreaming(method)) {
    printer->Print(vars,
                   "virtual ::rpc::Status $Method$("
                   "::rpc::ServerContext* context, "
                   "::rpc::ServerReader< $Request$>* reader, "
                   "$Response$* response);\n");
  } else if (IsOnlyClientStreaming(method)) {
    printer->Print(vars,
                   "virtual ::rpc::Status $Method$("
                   "::rpc::ServerContext* context, const $Request$* request, "
                   "::rpc::ServerWriter< $Response$>* writer);\n");
  } else if (method->IsBidiStreaming()) {
    printer->Print(vars,
                   "virtual ::rpc::Status $Method$("
                   "::rpc::ServerContext* context, "
                   "::rpc::ServerReaderWriter< $Response$, $Request$>* stream);"
                   "\n");
  }
  printer->Print(method->GetTrailingComments("//").c_str());
}

/*!
 * 打印头文件服务端异步方法
 * @param printer 打印器
 * @param method 方法
 * @param vars 参数
 */
void PrintHeaderServerMethodAsync(Printer *printer,
                                  const MethodInterface *method,
                                  std::map<std::string, std::string> &vars) {
  vars["Method"] = method->GetName();
  vars["Request"] = method->GetInputTypeClassName();
  vars["Response"] = method->GetOutputTypeClassName();
  printer->Print(vars, "template <class BaseClass>\n");
  printer->Print(vars,
                 "class WithAsyncMethod_$Method$ : public BaseClass {\n");
  printer->Print(
      " private:\n"
      "  void BaseClassMustBeDerivedFromService(const Service *service) {}\n");
  printer->Print(" public:\n");
  printer->Indent();
  printer->Print(vars,
                 "WithAsyncMethod_$Method$() {\n"
                 "  ::rpc::Service::MarkMethodAsync($Idx$);\n"
                 "}\n");
  printer->Print(vars,
                 "~WithAsyncMethod_$Method$() override {\n"
                 "  BaseClassMustBeDerivedFromService(this);\n"
                 "}\n");
  if (method->IsNoStreaming()) {
    printer->Print(
        vars,
        "// disable synchronous version of this method\n"
        "::rpc::Status $Method$("
        "::rpc::ServerContext* context, const $Request$* request, "
        "$Response$* response) final override {\n"
        "  abort();\n"
        "  return ::rpc::Status(::rpc::StatusCode::UNIMPLEMENTED, \"\");\n"
        "}\n");
    printer->Print(
        vars,
        "void Request$Method$("
        "::rpc::ServerContext* context, $Request$* request, "
        "::rpc::ServerAsyncResponseWriter< $Response$>* response, "
        "::rpc::CompletionQueue* new_call_cq, "
        "::rpc::ServerCompletionQueue* notification_cq, void *tag) {\n");
    printer->Print(vars,
                   "  ::rpc::Service::RequestAsyncUnary($Idx$, context, "
                   "request, response, new_call_cq, notification_cq, tag);\n");
    printer->Print("}\n");
  } else if (IsOnlyClientStreaming(method)) {
    printer->Print(
        vars,
        "// disable synchronous version of this method\n"
        "::rpc::Status $Method$("
        "::rpc::ServerContext* context, "
        "::rpc::ServerReader< $Request$>* reader, "
        "$Response$* response) final override {\n"
        "  abort();\n"
        "  return ::rpc::Status(::rpc::StatusCode::UNIMPLEMENTED, \"\");\n"
        "}\n");
    printer->Print(
        vars,
        "void Request$Method$("
        "::rpc::ServerContext* context, "
        "::rpc::ServerAsyncReader< $Response$, $Request$>* reader, "
        "::rpc::CompletionQueue* new_call_cq, "
        "::rpc::ServerCompletionQueue* notification_cq, void *tag) {\n");
    printer->Print(vars,
                   "  ::rpc::Service::RequestAsyncClientStreaming($Idx$, "
                   "context, reader, new_call_cq, notification_cq, tag);\n");
    printer->Print("}\n");
  } else if (IsOnlyServerStreaming(method)) {
    printer->Print(
        vars,
        "// disable synchronous version of this method\n"
        "::rpc::Status $Method$("
        "::rpc::ServerContext* context, const $Request$* request, "
        "::rpc::ServerWriter< $Response$>* writer) final override "
        "{\n"
        "  abort();\n"
        "  return ::rpc::Status(::rpc::StatusCode::UNIMPLEMENTED, \"\");\n"
        "}\n");
    printer->Print(
        vars,
        "void Request$Method$("
        "::rpc::ServerContext* context, $Request$* request, "
        "::rpc::ServerAsyncWriter< $Response$>* writer, "
        "::rpc::CompletionQueue* new_call_cq, "
        "::rpc::ServerCompletionQueue* notification_cq, void *tag) {\n");
    printer->Print(
        vars,
        "  ::rpc::Service::RequestAsyncServerStreaming($Idx$, "
        "context, request, writer, new_call_cq, notification_cq, tag);\n");
    printer->Print("}\n");
  } else if (method->IsBidiStreaming()) {
    printer->Print(
        vars,
        "// disable synchronous version of this method\n"
        "::rpc::Status $Method$("
        "::rpc::ServerContext* context, "
        "::rpc::ServerReaderWriter< $Response$, $Request$>* stream) "
        "final override {\n"
        "  abort();\n"
        "  return ::rpc::Status(::rpc::StatusCode::UNIMPLEMENTED, \"\");\n"
        "}\n");
    printer->Print(
        vars,
        "void Request$Method$("
        "::rpc::ServerContext* context, "
        "::rpc::ServerAsyncReaderWriter< $Response$, $Request$>* stream, "
        "::rpc::CompletionQueue* new_call_cq, "
        "::rpc::ServerCompletionQueue* notification_cq, void *tag) {\n");
    printer->Print(vars,
                   "  ::rpc::Service::RequestAsyncBidiStreaming($Idx$, "
                   "context, stream, new_call_cq, notification_cq, tag);\n");
    printer->Print("}\n");
  }
  printer->Outdent();
  printer->Print(vars, "};\n");
}

/*!
 * 打印头文件服务端单元缓冲流方法
 * @param printer 打印器
 * @param method 方法
 * @param vars 参数
 */
void PrintHeaderServerMethodStreamedUnary(Printer *printer,
                                          const MethodInterface *method,
                                          std::map<std::string,
                                                   std::string> &vars) {
  vars["Method"] = method->GetName();
  vars["Request"] = method->GetInputTypeClassName();
  vars["Response"] = method->GetOutputTypeClassName();
  if (method->IsNoStreaming()) {
    printer->Print(vars, "template <class BaseClass>\n");
    printer->Print(vars,
                   "class WithStreamedUnaryMethod_$Method$ : "
                   "public BaseClass {\n");
    printer->Print(
        " private:\n"
        "  void BaseClassMustBeDerivedFromService(const Service *service) "
        "{}\n");
    printer->Print(" public:\n");
    printer->Indent();
    printer->Print(vars,
                   "WithStreamedUnaryMethod_$Method$() {\n"
                   "  ::rpc::Service::MarkMethodStreamed($Idx$,\n"
                   "    new ::rpc::internal::StreamedUnaryHandler< $Request$, "
                   "$Response$>(std::bind"
                   "(&WithStreamedUnaryMethod_$Method$<BaseClass>::"
                   "Streamed$Method$, this, std::placeholders::_1, "
                   "std::placeholders::_2)));\n"
                   "}\n");
    printer->Print(vars,
                   "~WithStreamedUnaryMethod_$Method$() override {\n"
                   "  BaseClassMustBeDerivedFromService(this);\n"
                   "}\n");
    printer->Print(
        vars,
        "// disable regular version of this method\n"
        "::rpc::Status $Method$("
        "::rpc::ServerContext* context, const $Request$* request, "
        "$Response$* response) final override {\n"
        "  abort();\n"
        "  return ::rpc::Status(::rpc::StatusCode::UNIMPLEMENTED, \"\");\n"
        "}\n");
    printer->Print(vars,
                   "// replace default version of method with streamed unary\n"
                   "virtual ::rpc::Status Streamed$Method$("
                   "::rpc::ServerContext* context, "
                   "::rpc::ServerUnaryStreamer< "
                   "$Request$,$Response$>* server_unary_streamer)"
                   " = 0;\n");
    printer->Outdent();
    printer->Print(vars, "};\n");
  }
}

/*!
 * 打印头文件服务端多元缓冲流方法
 * @param printer 打印器
 * @param method 方法
 * @param vars 参数
 */
void PrintHeaderServerMethodSplitStreaming(Printer *printer,
                                           const MethodInterface *method,
                                           std::map<std::string,
                                                    std::string> &vars) {
  vars["Method"] = method->GetName();
  vars["Request"] = method->GetInputTypeClassName();
  vars["Response"] = method->GetOutputTypeClassName();
  if (IsOnlyServerStreaming(method)) {
    printer->Print(vars, "template <class BaseClass>\n");
    printer->Print(vars,
                   "class WithSplitStreamingMethod_$Method$ : "
                   "public BaseClass {\n");
    printer->Print(
        " private:\n"
        "  void BaseClassMustBeDerivedFromService(const Service *service) "
        "{}\n");
    printer->Print(" public:\n");
    printer->Indent();
    printer->Print(
        vars,
        "WithSplitStreamingMethod_$Method$() {\n"
        "  ::rpc::Service::MarkMethodStreamed($Idx$,\n"
        "    new ::rpc::internal::SplitServerStreamingHandler< $Request$, "
        "$Response$>(std::bind"
        "(&WithSplitStreamingMethod_$Method$<BaseClass>::"
        "Streamed$Method$, this, std::placeholders::_1, "
        "std::placeholders::_2)));\n"
        "}\n");
    printer->Print(vars,
                   "~WithSplitStreamingMethod_$Method$() override {\n"
                   "  BaseClassMustBeDerivedFromService(this);\n"
                   "}\n");
    printer->Print(
        vars,
        "// disable regular version of this method\n"
        "::rpc::Status $Method$("
        "::rpc::ServerContext* context, const $Request$* request, "
        "::rpc::ServerWriter< $Response$>* writer) final override "
        "{\n"
        "  abort();\n"
        "  return ::rpc::Status(::rpc::StatusCode::UNIMPLEMENTED, \"\");\n"
        "}\n");
    printer->Print(vars,
                   "// replace default version of method with split streamed\n"
                   "virtual ::rpc::Status Streamed$Method$("
                   "::rpc::ServerContext* context, "
                   "::rpc::ServerSplitStreamer< "
                   "$Request$,$Response$>* server_split_streamer)"
                   " = 0;\n");
    printer->Outdent();
    printer->Print(vars, "};\n");
  }
}

/*!
 * 打印头文件服务端通用方法
 * @param printer 打印器
 * @param method 方法
 * @param vars 参数
 */
void PrintHeaderServerMethodGeneric(Printer *printer,
                                    const MethodInterface *method,
                                    std::map<std::string, std::string> &vars) {
  vars["Method"] = method->GetName();
  vars["Request"] = method->GetInputTypeClassName();
  vars["Response"] = method->GetOutputTypeClassName();
  printer->Print(vars, "template <class BaseClass>\n");
  printer->Print(vars,
                 "class WithGenericMethod_$Method$ : public BaseClass {\n");
  printer->Print(
      " private:\n"
      "  void BaseClassMustBeDerivedFromService(const Service *service) {}\n");
  printer->Print(" public:\n");
  printer->Indent();
  printer->Print(vars,
                 "WithGenericMethod_$Method$() {\n"
                 "  ::rpc::Service::MarkMethodGeneric($Idx$);\n"
                 "}\n");
  printer->Print(vars,
                 "~WithGenericMethod_$Method$() override {\n"
                 "  BaseClassMustBeDerivedFromService(this);\n"
                 "}\n");
  if (method->IsNoStreaming()) {
    printer->Print(
        vars,
        "// disable synchronous version of this method\n"
        "::rpc::Status $Method$("
        "::rpc::ServerContext* context, const $Request$* request, "
        "$Response$* response) final override {\n"
        "  abort();\n"
        "  return ::rpc::Status(::rpc::StatusCode::UNIMPLEMENTED, \"\");\n"
        "}\n");
  } else if (IsOnlyClientStreaming(method)) {
    printer->Print(
        vars,
        "// disable synchronous version of this method\n"
        "::rpc::Status $Method$("
        "::rpc::ServerContext* context, "
        "::rpc::ServerReader< $Request$>* reader, "
        "$Response$* response) final override {\n"
        "  abort();\n"
        "  return ::rpc::Status(::rpc::StatusCode::UNIMPLEMENTED, \"\");\n"
        "}\n");
  } else if (IsOnlyServerStreaming(method)) {
    printer->Print(
        vars,
        "// disable synchronous version of this method\n"
        "::rpc::Status $Method$("
        "::rpc::ServerContext* context, const $Request$* request, "
        "::rpc::ServerWriter< $Response$>* writer) final override "
        "{\n"
        "  abort();\n"
        "  return ::rpc::Status(::rpc::StatusCode::UNIMPLEMENTED, \"\");\n"
        "}\n");
  } else if (method->IsBidiStreaming()) {
    printer->Print(
        vars,
        "// disable synchronous version of this method\n"
        "::rpc::Status $Method$("
        "::rpc::ServerContext* context, "
        "::rpc::ServerReaderWriter< $Response$, $Request$>* stream) "
        "final override {\n"
        "  abort();\n"
        "  return ::rpc::Status(::rpc::StatusCode::UNIMPLEMENTED, \"\");\n"
        "}\n");
  }
  printer->Outdent();
  printer->Print(vars, "};\n");
}

/*!
 * 打印头文件服务端服务
 * @param printer 打印器
 * @param method 方法
 * @param vars 参数
 */
void PrintHeaderService(Printer *printer,
                        const ServiceInterface *service,
                        std::map<std::string, std::string> &vars) {
  vars["Service"] = service->GetName();

  printer->Print(service->GetLeadingComments("//").c_str());
  printer->Print(vars, "class $Service$_Stub;\n\n");
  printer->Print(vars,
                 "class $Service$ : public ::google::protobuf::Service {\n"
                 " public:\n");
  printer->Indent();

  printer->Print(vars,
                 "virtual ~$Service$() {}\n\n"
                 "typedef $Service$_Stub Stub;\n\n");

  printer->Print(
      "static const ::google::protobuf::ServiceDescriptor* descriptor();\n\n");

  for (int i = 0; i < service->GetMethodCount(); ++i) {
    printer->Print(service->GetMethodByIndex(i)->GetLeadingComments("//").c_str());
    PrintHeaderClientMethodInterfaces(printer,
                                      service->GetMethodByIndex(i).get(),
                                      vars,
                                      true);
    printer->Print(service->GetMethodByIndex(i)->GetTrailingComments("//").c_str());
  }

  printer->Print("// implements Service\n\n");

  printer->Print(
      "const ::google::protobuf::ServiceDescriptor* GetDescriptor();\n");
  printer->Print(
      "void CallMethod(const ::google::protobuf::MethodDescriptor* method,\n"
      "  ::google::protobuf::RpcController* controller,\n"
      "  const ::google::protobuf::Message* request,\n"
      "  ::google::protobuf::Message* response,\n"
      "  ::google::protobuf::Closure* done);\n");
  printer->Print("const ::google::protobuf::Message& GetRequestPrototype(\n"
                 "  const ::google::protobuf::MethodDescriptor* method) const;\n");
  printer->Print("const ::google::protobuf::Message& GetResponsePrototype(\n"
                 "  const ::google::protobuf::MethodDescriptor* method) const;\n\n");
  printer->Outdent();

  printer->Print(" private:\n");
  printer->Indent();

  printer->Print(vars, "GOOGLE_DISALLOW_EVIL_CONSTRUCTORS($Service$);\n");
  printer->Outdent();

  printer->Print("};\n\n");

  printer->Print(vars,
                 "class $Service$_Stub : public $Service$ {\n"
                 " public:\n");
  printer->Indent();

  printer->Print(vars,
                 "$Service$_Stub(::google::protobuf::RpcChannel* channel);\n"
                 "$Service$_Stub(::google::protobuf::RpcChannel* channel,\n"
                 "    ::google::protobuf::Service::ChannelOwnership ownership);\n"
                 "~$Service$_Stub();\n\n");

  printer->Print(
      "inline ::google::protobuf::RpcChannel* channel() { return channel_; }\n\n");

  printer->Print(vars, "// implements $Service$\n\n");
  for (int i = 0; i < service->GetMethodCount(); ++i) {
    PrintHeaderClientMethodInterfaces(printer,
                                      service->GetMethodByIndex(i).get(),
                                      vars,
                                      false);
  }

  printer->Outdent();

  printer->Print(" private:\n");
  printer->Indent();

  printer->Print(vars,
                 "::google::protobuf::RpcChannel* channel_;\n"
                 "bool owns_channel_;\n"
                 "GOOGLE_DISALLOW_EVIL_CONSTRUCTORS($Service$_Stub);\n");
  printer->Outdent();

  printer->Print("};\n");
}

/*!
 * 打印源文件客户端方法
 * @param printer 打印器
 * @param method 方法
 * @param vars 参数
 */
void PrintSourceClientMethod(Printer *printer,
                             const MethodInterface *method,
                             std::map<std::string, std::string> &vars) {
  vars["Method"] = method->GetName();
  vars["Request"] = method->GetInputTypeClassName();
  vars["Response"] = method->GetOutputTypeClassName();
  struct {
    std::string prefix;
    std::string start;          // bool literal expressed as string
    std::string method_params;  // extra arguments to method
    std::string create_args;    // extra arguments to creator
  } async_prefixes[] = {{"Async", "true", ", void* tag", ", tag"},
                        {"PrepareAsync", "false", "", ", nullptr"}};
  if (method->IsNoStreaming()) {
    printer->Print(vars,
                   "::rpc::Status $ns$$Service$::Stub::$Method$("
                   "::rpc::ClientContext* context, "
                   "const $Request$& request, $Response$* response) {\n");
    printer->Print(vars,
                   "  return ::rpc::internal::BlockingUnaryCall"
                   "(channel_.get(), rpcmethod_$Method$_, "
                   "context, request, response);\n}\n\n");
    for (auto async_prefix : async_prefixes) {
      vars["AsyncPrefix"] = async_prefix.prefix;
      vars["AsyncStart"] = async_prefix.start;
      printer->Print(vars,
                     "::rpc::ClientAsyncResponseReader< $Response$>* "
                     "$ns$$Service$::Stub::$AsyncPrefix$$Method$Raw(::rpc::"
                     "ClientContext* context, "
                     "const $Request$& request, "
                     "::rpc::CompletionQueue* cq) {\n");
      printer->Print(
          vars,
          "  return "
          "::rpc::internal::ClientAsyncResponseReaderFactory< $Response$>"
          "::Create(channel_.get(), cq, "
          "rpcmethod_$Method$_, "
          "context, request, $AsyncStart$);\n"
          "}\n\n");
    }
  } else if (IsOnlyClientStreaming(method)) {
    printer->Print(vars,
                   "::rpc::ClientWriter< $Request$>* "
                   "$ns$$Service$::Stub::$Method$Raw("
                   "::rpc::ClientContext* context, $Response$* response) {\n");
    printer->Print(
        vars,
        "  return ::rpc::internal::ClientWriterFactory< $Request$>::Create("
        "channel_.get(), "
        "rpcmethod_$Method$_, "
        "context, response);\n"
        "}\n\n");
    for (auto async_prefix : async_prefixes) {
      vars["AsyncPrefix"] = async_prefix.prefix;
      vars["AsyncStart"] = async_prefix.start;
      vars["AsyncMethodParams"] = async_prefix.method_params;
      vars["AsyncCreateArgs"] = async_prefix.create_args;
      printer->Print(vars,
                     "::rpc::ClientAsyncWriter< $Request$>* "
                     "$ns$$Service$::Stub::$AsyncPrefix$$Method$Raw("
                     "::rpc::ClientContext* context, $Response$* response, "
                     "::rpc::CompletionQueue* cq$AsyncMethodParams$) {\n");
      printer->Print(
          vars,
          "  return ::rpc::internal::ClientAsyncWriterFactory< $Request$>"
          "::Create(channel_.get(), cq, "
          "rpcmethod_$Method$_, "
          "context, response, $AsyncStart$$AsyncCreateArgs$);\n"
          "}\n\n");
    }
  } else if (IsOnlyServerStreaming(method)) {
    printer->Print(
        vars,
        "::rpc::ClientReader< $Response$>* "
        "$ns$$Service$::Stub::$Method$Raw("
        "::rpc::ClientContext* context, const $Request$& request) {\n");
    printer->Print(
        vars,
        "  return ::rpc::internal::ClientReaderFactory< $Response$>::Create("
        "channel_.get(), "
        "rpcmethod_$Method$_, "
        "context, request);\n"
        "}\n\n");
    for (auto async_prefix : async_prefixes) {
      vars["AsyncPrefix"] = async_prefix.prefix;
      vars["AsyncStart"] = async_prefix.start;
      vars["AsyncMethodParams"] = async_prefix.method_params;
      vars["AsyncCreateArgs"] = async_prefix.create_args;
      printer->Print(
          vars,
          "::rpc::ClientAsyncReader< $Response$>* "
          "$ns$$Service$::Stub::$AsyncPrefix$$Method$Raw("
          "::rpc::ClientContext* context, const $Request$& request, "
          "::rpc::CompletionQueue* cq$AsyncMethodParams$) {\n");
      printer->Print(
          vars,
          "  return ::rpc::internal::ClientAsyncReaderFactory< $Response$>"
          "::Create(channel_.get(), cq, "
          "rpcmethod_$Method$_, "
          "context, request, $AsyncStart$$AsyncCreateArgs$);\n"
          "}\n\n");
    }
  } else if (method->IsBidiStreaming()) {
    printer->Print(
        vars,
        "::rpc::ClientReaderWriter< $Request$, $Response$>* "
        "$ns$$Service$::Stub::$Method$Raw(::rpc::ClientContext* context) {\n");
    printer->Print(vars,
                   "  return ::rpc::internal::ClientReaderWriterFactory< "
                   "$Request$, $Response$>::Create("
                   "channel_.get(), "
                   "rpcmethod_$Method$_, "
                   "context);\n"
                   "}\n\n");
    for (auto async_prefix : async_prefixes) {
      vars["AsyncPrefix"] = async_prefix.prefix;
      vars["AsyncStart"] = async_prefix.start;
      vars["AsyncMethodParams"] = async_prefix.method_params;
      vars["AsyncCreateArgs"] = async_prefix.create_args;
      printer->Print(vars,
                     "::rpc::ClientAsyncReaderWriter< $Request$, $Response$>* "
                     "$ns$$Service$::Stub::$AsyncPrefix$$Method$Raw(::rpc::"
                     "ClientContext* context, "
                     "::rpc::CompletionQueue* cq$AsyncMethodParams$) {\n");
      printer->Print(vars,
                     "  return "
                     "::rpc::internal::ClientAsyncReaderWriterFactory< "
                     "$Request$, $Response$>::Create("
                     "channel_.get(), cq, "
                     "rpcmethod_$Method$_, "
                     "context, $AsyncStart$$AsyncCreateArgs$);\n"
                     "}\n\n");
    }
  }
}

/*!
 * 打印源文件服务器方法
 * @param printer 打印器
 * @param method 方法
 * @param vars 参数
 */
void PrintSourceServerMethod(Printer *printer,
                             const MethodInterface *method,
                             std::map<std::string, std::string> &vars) {
  vars["Method"] = method->GetName();
  vars["Request"] = method->GetInputTypeClassName();
  vars["Response"] = method->GetOutputTypeClassName();
  if (method->IsNoStreaming()) {
    printer->Print(vars,
                   "::rpc::Status $ns$$Service$::Service::$Method$("
                   "::rpc::ServerContext* context, "
                   "const $Request$* request, $Response$* response) {\n");
    printer->Print("  (void) context;\n");
    printer->Print("  (void) request;\n");
    printer->Print("  (void) response;\n");
    printer->Print(
        "  return ::rpc::Status("
        "::rpc::StatusCode::UNIMPLEMENTED, \"\");\n");
    printer->Print("}\n\n");
  } else if (IsOnlyClientStreaming(method)) {
    printer->Print(vars,
                   "::rpc::Status $ns$$Service$::Service::$Method$("
                   "::rpc::ServerContext* context, "
                   "::rpc::ServerReader< $Request$>* reader, "
                   "$Response$* response) {\n");
    printer->Print("  (void) context;\n");
    printer->Print("  (void) reader;\n");
    printer->Print("  (void) response;\n");
    printer->Print(
        "  return ::rpc::Status("
        "::rpc::StatusCode::UNIMPLEMENTED, \"\");\n");
    printer->Print("}\n\n");
  } else if (IsOnlyServerStreaming(method)) {
    printer->Print(vars,
                   "::rpc::Status $ns$$Service$::Service::$Method$("
                   "::rpc::ServerContext* context, "
                   "const $Request$* request, "
                   "::rpc::ServerWriter< $Response$>* writer) {\n");
    printer->Print("  (void) context;\n");
    printer->Print("  (void) request;\n");
    printer->Print("  (void) writer;\n");
    printer->Print(
        "  return ::rpc::Status("
        "::rpc::StatusCode::UNIMPLEMENTED, \"\");\n");
    printer->Print("}\n\n");
  } else if (method->IsBidiStreaming()) {
    printer->Print(vars,
                   "::rpc::Status $ns$$Service$::Service::$Method$("
                   "::rpc::ServerContext* context, "
                   "::rpc::ServerReaderWriter< $Response$, $Request$>* "
                   "stream) {\n");
    printer->Print("  (void) context;\n");
    printer->Print("  (void) stream;\n");
    printer->Print(
        "  return ::rpc::Status("
        "::rpc::StatusCode::UNIMPLEMENTED, \"\");\n");
    printer->Print("}\n\n");
  }
}

/*!
 * 打印源文件服务
 * @param printer 打印器
 * @param method 方法
 * @param vars 参数
 */
void PrintSourceService(Printer *printer,
                        const ServiceInterface *service,
                        std::map<std::string, std::string> &vars) {
  vars["Service"] = service->GetName();

  printer->Print(vars,
                 "const ::google::protobuf::ServiceDescriptor* $Service$::descriptor() {\n"
                 "  protobuf_Proxy_2eproto::protobuf_AssignDescriptorsOnce();\n"
                 "  return protobuf_Proxy_2eproto::file_level_service_descriptors[0];\n"
                 "}\n\n");

  printer->Print(vars,
                 "const ::google::protobuf::ServiceDescriptor* $Service$::GetDescriptor() {\n"
                 "  return descriptor();\n"
                 "}\n\n");

  printer->Print(
      "void ProxyService::run(::google::protobuf::RpcController* controller,\n");
  printer->Print("                         const ::asfinger::pbrpc::Query*,\n");
  printer->Print("                         ::asfinger::pbrpc::Result*,\n");
  printer->Print(
      "                         ::google::protobuf::Closure* done) {\n");
  printer->Print("  controller->SetFailed(\"Method run() not implemented.\");\n");
  printer->Print("  done->Run();\n");
  printer->Print("}\n\n");

  printer->Print(
      "void ProxyService::CallMethod(const ::google::protobuf::MethodDescriptor* method,\n");
  printer->Print(
      "                             ::google::protobuf::RpcController* controller,\n");
  printer->Print(
      "                             const ::google::protobuf::Message* request,\n");
  printer->Print(
      "                             ::google::protobuf::Message* response,\n");
  printer->Print(
      "                             ::google::protobuf::Closure* done) {\n");
  printer->Print(
      "  GOOGLE_DCHECK_EQ(method->service(), protobuf_Proxy_2eproto::file_level_service_descriptors[0]);\n");
  printer->Print("  switch(method->index()) {\n");
  printer->Print("    case 0:\n");
  printer->Print("      run(controller,\n");
  printer->Print(
      "             ::google::protobuf::down_cast<const ::asfinger::pbrpc::Query*>(request),\n");
  printer->Print(
      "             ::google::protobuf::down_cast< ::asfinger::pbrpc::Result*>(response),\n");
  printer->Print("             done);\n");
  printer->Print("      break;\n");
  printer->Print("    default:\n");
  printer->Print(
      "      GOOGLE_LOG(FATAL) << \"Bad method index; this should never happen.\";\n");
  printer->Print("      break;\n");
  printer->Print("  }\n");
  printer->Print("}\n\n");

  printer->Print(
      "const ::google::protobuf::Message& ProxyService::GetRequestPrototype(\n");
  printer->Print(
      "    const ::google::protobuf::MethodDescriptor* method) const {\n");
  printer->Print("  GOOGLE_DCHECK_EQ(method->service(), descriptor());\n");
  printer->Print("  switch(method->index()) {\n");
  printer->Print("    case 0:\n");
  printer->Print("      return ::asfinger::pbrpc::Query::default_instance();\n");
  printer->Print("    default:\n");
  printer->Print(
      "      GOOGLE_LOG(FATAL) << \"Bad method index; this should never happen.\";\n");
  printer->Print(
      "      return *::google::protobuf::MessageFactory::generated_factory()\n");
  printer->Print("          ->GetPrototype(method->input_type());\n");
  printer->Print("  }\n");
  printer->Print("}\n");
  printer->Print("\n");
  printer->Print(
      "const ::google::protobuf::Message& ProxyService::GetResponsePrototype(\n");
  printer->Print(
      "    const ::google::protobuf::MethodDescriptor* method) const {\n");
  printer->Print("  GOOGLE_DCHECK_EQ(method->service(), descriptor());\n");
  printer->Print("  switch(method->index()) {\n");
  printer->Print("    case 0:\n");
  printer->Print("      return ::asfinger::pbrpc::Result::default_instance();\n");
  printer->Print("    default:\n");
  printer->Print(
      "      GOOGLE_LOG(FATAL) << \"Bad method index; this should never happen.\";\n");
  printer->Print(
      "      return *::google::protobuf::MessageFactory::generated_factory()\n");
  printer->Print("          ->GetPrototype(method->output_type());\n");
  printer->Print("  }\n");
  printer->Print("}\n");
  printer->Print("\n");
  printer->Print(
      "ProxyService_Stub::ProxyService_Stub(::google::protobuf::RpcChannel* channel)\n");
  printer->Print("  : channel_(channel), owns_channel_(false) {}\n");
  printer->Print("ProxyService_Stub::ProxyService_Stub(\n");
  printer->Print("    ::google::protobuf::RpcChannel* channel,\n");
  printer->Print(
      "    ::google::protobuf::Service::ChannelOwnership ownership)\n");
  printer->Print("  : channel_(channel),\n");
  printer->Print(
      "    owns_channel_(ownership == ::google::protobuf::Service::STUB_OWNS_CHANNEL) {}\n");
  printer->Print("ProxyService_Stub::~ProxyService_Stub() {\n");
  printer->Print("  if (owns_channel_) delete channel_;\n");
  printer->Print("}\n");
  printer->Print("\n");
  printer->Print(
      "void ProxyService_Stub::run(::google::protobuf::RpcController* controller,\n");
  printer->Print(
      "                              const ::asfinger::pbrpc::Query* request,\n");
  printer->Print(
      "                              ::asfinger::pbrpc::Result* response,\n");
  printer->Print(
      "                              ::google::protobuf::Closure* done) {\n");
  printer->Print("  channel_->CallMethod(descriptor()->method(0),\n");
  printer->Print(
      "                       controller, request, response, done);\n");
  printer->Print("}\n");

  if (service->GetMethodCount() > 0) {
    printer->Print(vars,
                   "static const char* $prefix$$Service$_method_names[] = {\n");
    for (int i = 0; i < service->GetMethodCount(); ++i) {
      vars["Method"] = service->GetMethodByIndex(i).get()->GetName();
      printer->Print(vars, "  \"/$Package$$Service$/$Method$\",\n");
    }
    printer->Print(vars, "};\n\n");
  }

  printer->Print(vars,
                 "std::unique_ptr< $ns$$Service$::Stub> $ns$$Service$::NewStub("
                 "const std::shared_ptr< ::rpc::ChannelInterface>& channel, "
                 "const ::rpc::StubOptions& options) {\n"
                 "  (void)options;\n"
                 "  std::unique_ptr< $ns$$Service$::Stub> stub(new "
                 "$ns$$Service$::Stub(channel));\n"
                 "  return stub;\n"
                 "}\n\n");
  printer->Print(vars,
                 "$ns$$Service$::Stub::Stub(const std::shared_ptr< "
                 "::rpc::ChannelInterface>& channel)\n");
  printer->Indent();
  printer->Print(": channel_(channel)");
  for (int i = 0; i < service->GetMethodCount(); ++i) {
    auto method = service->GetMethodByIndex(i);
    vars["Method"] = method->GetName();
    vars["Idx"] = AsString(i);
    if (method->IsNoStreaming()) {
      vars["StreamingType"] = "NORMAL_RPC";
      // NOTE: There is no reason to consider streamed-unary as a separate
      // category here since this part is setting up the client-side stub
      // and this appears as a NORMAL_RPC from the client-side.
    } else if (IsOnlyClientStreaming(method.get())) {
      vars["StreamingType"] = "CLIENT_STREAMING";
    } else if (IsOnlyServerStreaming(method.get())) {
      vars["StreamingType"] = "SERVER_STREAMING";
    } else {
      vars["StreamingType"] = "BIDI_STREAMING";
    }
    printer->Print(vars,
                   ", rpcmethod_$Method$_("
                   "$prefix$$Service$_method_names[$Idx$], "
                   "::rpc::internal::RpcMethod::$StreamingType$, "
                   "channel"
                   ")\n");
  }
  printer->Print("{}\n\n");
  printer->Outdent();

  for (int i = 0; i < service->GetMethodCount(); ++i) {
    vars["Idx"] = AsString(i);
    PrintSourceClientMethod(printer, service->GetMethodByIndex(i).get(), vars);
  }

  printer->Print(vars, "$ns$$Service$::Service::Service() {\n");
  printer->Indent();
  for (int i = 0; i < service->GetMethodCount(); ++i) {
    auto method = service->GetMethodByIndex(i);
    vars["Idx"] = AsString(i);
    vars["Method"] = method->GetName();
    vars["Request"] = method->GetInputTypeClassName();
    vars["Response"] = method->GetOutputTypeClassName();
    if (method->IsNoStreaming()) {
      printer->Print(
          vars,
          "AddMethod(new ::rpc::internal::RpcServiceMethod(\n"
          "    $prefix$$Service$_method_names[$Idx$],\n"
          "    ::rpc::internal::RpcMethod::NORMAL_RPC,\n"
          "    new ::rpc::internal::RpcMethodHandler< $ns$$Service$::Service, "
          "$Request$, "
          "$Response$>(\n"
          "        std::mem_fn(&$ns$$Service$::Service::$Method$), this)));\n");
    } else if (IsOnlyClientStreaming(method.get())) {
      printer->Print(
          vars,
          "AddMethod(new ::rpc::internal::RpcServiceMethod(\n"
          "    $prefix$$Service$_method_names[$Idx$],\n"
          "    ::rpc::internal::RpcMethod::CLIENT_STREAMING,\n"
          "    new ::rpc::internal::ClientStreamingHandler< "
          "$ns$$Service$::Service, $Request$, $Response$>(\n"
          "        std::mem_fn(&$ns$$Service$::Service::$Method$), this)));\n");
    } else if (IsOnlyServerStreaming(method.get())) {
      printer->Print(
          vars,
          "AddMethod(new ::rpc::internal::RpcServiceMethod(\n"
          "    $prefix$$Service$_method_names[$Idx$],\n"
          "    ::rpc::internal::RpcMethod::SERVER_STREAMING,\n"
          "    new ::rpc::internal::ServerStreamingHandler< "
          "$ns$$Service$::Service, $Request$, $Response$>(\n"
          "        std::mem_fn(&$ns$$Service$::Service::$Method$), this)));\n");
    } else if (method->IsBidiStreaming()) {
      printer->Print(
          vars,
          "AddMethod(new ::rpc::internal::RpcServiceMethod(\n"
          "    $prefix$$Service$_method_names[$Idx$],\n"
          "    ::rpc::internal::RpcMethod::BIDI_STREAMING,\n"
          "    new ::rpc::internal::BidiStreamingHandler< "
          "$ns$$Service$::Service, $Request$, $Response$>(\n"
          "        std::mem_fn(&$ns$$Service$::Service::$Method$), this)));\n");
    }
  }
  printer->Outdent();
  printer->Print(vars, "}\n\n");
  printer->Print(vars,
                 "$ns$$Service$::Service::~Service() {\n"
                 "}\n\n");
  for (int i = 0; i < service->GetMethodCount(); ++i) {
    vars["Idx"] = AsString(i);
    PrintSourceServerMethod(printer, service->GetMethodByIndex(i).get(), vars);
  }
}

std::string GetHeaderPrologue(FileInterface *file, const Parameters &params) {
  std::string output;
  {
    auto printer = file->CreatePrinter(output);
    std::map<std::string, std::string> vars;

    vars["filename"] = file->GetFileName();
    vars["filename_identifier"] = FilenameIdentifier(file->GetFileName());
    vars["filename_base"] = file->GetFileNameWithoutExt();
    vars["message_header_ext"] = kCppGeneratorMessageHeaderExt;

    printer->Print(vars,
                   "// Generated by the asfinger pbrpc C++ plugin.\n");
    printer->Print(vars,
                   "// DO NOT EDIT!\n");
    printer->Print(vars,
                   "// source: $filename$\n");
    std::string leading_comments = file->GetLeadingComments("//");
    if (!leading_comments.empty()) {
      printer->Print(vars,
                     "#ifndef ASFINGER_PBRPC_$filename_identifier$__INCLUDED\n");
      printer->Print(vars,
                     "#define ASFINGER_PBRPC_$filename_identifier$__INCLUDED\n");
      printer->Print(vars, "\n");
      printer->Print(vars,
                     "#include \"$filename_base$$message_header_ext$\"\n");
      printer->Print(vars, file->AdditionalHeaders().c_str());
      printer->Print(vars, "\n");
    }
  }
  return output;
}

std::string GetHeaderIncludes(FileInterface *file, const Parameters &params) {
  std::string output;
  {
    auto printer = file->CreatePrinter(output);
    std::map<std::string, std::string> vars;

    if (!params.additional_header_includes.empty()) {
      PrintIncludes(printer.get(),
                    params.additional_header_includes,
                    false,
                    "");
    }

    static const char *headers_strs[] = {"google/protobuf/service.h"};

    std::vector<std::string> headers(headers_strs, ArrayEnd(headers_strs));
    PrintIncludes(printer.get(),
                  headers,
                  params.use_system_headers,
                  params.rpc_search_path);
    printer->Print(vars, "\n");

    if (!file->GetPackage().empty()) {
      std::vector<std::string> parts = file->GetPackageParts();

      for (auto &part : parts) {
        vars["part"] = part;
        printer->Print(vars, "namespace $part$ {\n");
      }
      printer->Print(vars, "\n");
    }
  }
  return output;
}

std::string GetHeaderServices(FileInterface *file, const Parameters &params) {
  std::string output;
  {
    // Scope the output stream so it closes and finalizes output to the string.
    auto printer = file->CreatePrinter(output);
    std::map<std::string, std::string> vars;
    // Package string is empty or ends with a dot. It is used to fully qualify
    // method names.
    vars["Package"] = file->GetPackage();
    if (!file->GetPackage().empty()) {
      vars["Package"].append(".");
    }

    if (!params.services_namespace.empty()) {
      vars["services_namespace"] = params.services_namespace;
      printer->Print(vars, "\nnamespace $services_namespace$ {\n\n");
    }

    for (int i = 0; i < file->GetServiceCount(); ++i) {
      PrintHeaderService(printer.get(), file->GetServiceByIndex(i).get(), vars);
      printer->Print("\n");
    }

    if (!params.services_namespace.empty()) {
      printer->Print(vars, "}  // namespace $services_namespace$\n\n");
    }
  }
  return output;
}

std::string GetHeaderEpilogue(FileInterface *file, const Parameters &params) {
  std::string output;
  {
    // Scope the output stream so it closes and finalizes output to the string.
    auto printer = file->CreatePrinter(output);
    std::map<std::string, std::string> vars;

    vars["filename"] = file->GetFileName();
    vars["filename_identifier"] = FilenameIdentifier(file->GetFileName());

    if (!file->GetPackage().empty()) {
      std::vector<std::string> parts = file->GetPackageParts();

      for (auto part = parts.rbegin(); part != parts.rend(); part++) {
        vars["part"] = *part;
        printer->Print(vars, "}  // namespace $part$\n");
      }
      printer->Print(vars, "\n");
    }

    printer->Print(vars, "\n");
    printer->Print(vars, "#endif  // RPC_$filename_identifier$__INCLUDED\n");

    printer->Print(file->GetTrailingComments("//").c_str());
  }
  return output;
}

std::string GetSourcePrologue(FileInterface *file, const Parameters &params) {
  std::string output;
  {
    // Scope the output stream so it closes and finalizes output to the string.
    auto printer = file->CreatePrinter(output);
    std::map<std::string, std::string> vars;

    vars["filename"] = file->GetFileName();
    vars["filename_base"] = file->GetFileNameWithoutExt();
    vars["message_header_ext"] = kCppGeneratorMessageHeaderExt;
    vars["service_header_ext"] = kCppGeneratorServiceHeaderExt;

    printer->Print(vars, "// Generated by the raster pbrpc C++ plugin.\n");
    printer->Print(vars,
                   "// If you make any local change, they will be lost.\n");
    printer->Print(vars, "// source: $filename$\n\n");

    printer->Print(vars, "#include \"$filename_base$$message_header_ext$\"\n");
    printer->Print(vars, "#include \"$filename_base$$service_header_ext$\"\n");
    printer->Print(vars, "\n");
  }
  return output;
}

std::string GetSourceIncludes(FileInterface *file, const Parameters &params) {
  std::string output;
  {
    // Scope the output stream so it closes and finalizes output to the string.
    auto printer = file->CreatePrinter(output);
    std::map<std::string, std::string> vars;

    if (!file->GetPackage().empty()) {
      std::vector<std::string> parts = file->GetPackageParts();

      for (auto part = parts.begin(); part != parts.end(); part++) {
        vars["part"] = *part;
        printer->Print(vars, "namespace $part$ {\n");
      }
    }

    printer->Print(vars, "\n");
  }
  return output;
}

std::string GetSourceServices(FileInterface *file, const Parameters &params) {
  std::string output;
  {
    // Scope the output stream so it closes and finalizes output to the string.
    auto printer = file->CreatePrinter(output);
    std::map<std::string, std::string> vars;
    // Package string is empty or ends with a dot. It is used to fully qualify
    // method names.
    vars["Package"] = file->GetPackage();
    if (!file->GetPackage().empty()) {
      vars["Package"].append(".");
    }
    if (!params.services_namespace.empty()) {
      vars["ns"] = params.services_namespace + "::";
      vars["prefix"] = params.services_namespace;
    } else {
      vars["ns"] = "";
      vars["prefix"] = "";
    }

    for (int i = 0; i < file->GetServiceCount(); ++i) {
      PrintSourceService(printer.get(), file->GetServiceByIndex(i).get(), vars);
      printer->Print("\n");
    }
  }
  return output;
}

std::string GetSourceEpilogue(FileInterface *file, const Parameters &params) {
  std::string temp;

  if (!file->GetPackage().empty()) {
    std::vector<std::string> parts = file->GetPackageParts();

    for (auto part = parts.begin(); part != parts.end(); part++) {
      temp.append("}  // namespace ");
      temp.append(*part);
      temp.append("\n");
    }
    temp.append("\n");
  }

  return temp;
}

} //! namespace pbrpc
} //! namespace asfinger